package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.srb.common.exception.BusinessException;
import com.atguigu.srb.core.enums.LendStatusEnum;
import com.atguigu.srb.core.enums.ReturnMethodEnum;
import com.atguigu.srb.core.enums.TransTypeEnum;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.mapper.*;
import com.atguigu.srb.core.pojo.bo.TransFlowBO;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.atguigu.srb.core.pojo.vo.BorrowerDetailVO;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.utils.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author javaCode
 * @since 2025-07-19
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {

    private final DictService dictService;
    private final BorrowerMapper borrowerMapper;
    private final BorrowerService borrowerService;
    private final UserInfoMapper userInfoMapper;
    private final UserAccountMapper userAccountMapper;
    private final TransFlowService transFlowService;
    private final LendItemMapper lendItemMapper;
    private final LendReturnService lendReturnService;
    private final LendItemReturnService lendItemReturnService;

    /**
     * 创建标的
     * 根据借款审批信息和借款基本信息创建投资标的
     * 标的信息将记录到数据库并进入投资流程
     *
     * @param borrowInfoApprovalVO 借款审批信息，包含审批通过的利率、期限等关键信息
     * @param borrowInfo           借款基本信息，包含借款人、金额、还款方式等基础信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
        // 1. 初始化标的对象
        Lend lend = new Lend();

        // 2. 设置基本信息
        lend.setUserId(borrowInfo.getUserId());                // 关联借款人ID
        lend.setBorrowInfoId(borrowInfo.getId());              // 关联借款申请ID
        lend.setLendNo(LendNoUtils.getLendNo());               // 生成唯一标的编号
        lend.setTitle(borrowInfoApprovalVO.getTitle());        // 标的标题
        lend.setAmount(borrowInfo.getAmount());                // 标的金额
        lend.setPeriod(borrowInfo.getPeriod());                // 借款期限（月）
        lend.setLendYearRate(borrowInfoApprovalVO.getLendYearRate().divide(new BigDecimal(100))); // 年化利率（转换为小数形式）
        lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(100)));   // 服务费率（转换为小数形式）
        lend.setReturnMethod(borrowInfo.getReturnMethod());    // 还款方式
        lend.setPublishDate(LocalDateTime.now());              // 发布时间

        // 3. 设置投资相关信息
        lend.setLowestAmount(new BigDecimal(100));             // 最低投资金额
        lend.setInvestAmount(new BigDecimal(0));               // 已投资金额（初始为0）
        lend.setInvestNum(0);                                    // 投资人数（初始为0）

        // 4. 设置时间信息
        // 起息日期（从审批信息中获取，格式转换）
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate lendStartDate = LocalDate.parse(borrowInfoApprovalVO.getLendStartDate(), dtf);
        lend.setLendStartDate(lendStartDate);

        // 结束日期（起息日 + 借款期数）
        LocalDate lendEndDate = lendStartDate.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(lendEndDate);

        // 5. 设置标的描述信息
        lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());  // 标的详细描述

        // 6. 计算平台预期收益
        // 月年化 = 年化服务费率 / 12
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);

        // 平台预期收益 = 标的金额 * 月年化 * 期数
        BigDecimal expectAmount = lend.getAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        lend.setExpectAmount(expectAmount);

        // 7. 设置实际收益（初始为0，随还款动态更新）
        lend.setRealAmount(new BigDecimal(0));

        // 8. 设置标的状态为"投资中"
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());

        // 9. 设置审核信息
        lend.setCheckTime(LocalDateTime.now());                  // 审核时间
        lend.setCheckAdminId(1L);                                // 审核人ID（默认1）

        // 10. 保存标的信息到数据库
        baseMapper.insert(lend);
    }

    /**
     * 获取标的列表
     * 查询所有标的信息并进行业务状态转换
     *
     * @return 标的列表，包含转换后的业务状态描述
     */
    @Override
    public List<Lend> selectList() {
        // 1. 从数据库查询所有标的记录
        List<Lend> lendList = baseMapper.selectList(null);

        // 2. 遍历标的列表，处理业务状态转换
        lendList.forEach(lend -> {
            // 2.1 获取还款方式的中文描述（通过数据字典服务）
            // 参数说明："returnMethod"为父字典编码，lend.getReturnMethod()为具体值
            String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());

            // 2.2 获取标的状态的中文描述（通过枚举类）
            // 根据标的状态码从枚举类中获取对应的中文描述
            String status = LendStatusEnum.getMsgByStatus(lend.getStatus());

            // 2.3 将转换后的业务状态存入扩展参数Map
            // 扩展参数Map用于前端展示，避免修改实体类结构
            lend.getParam().put("returnMethod", returnMethod);
            lend.getParam().put("status", status);
        });

        // 3. 返回处理后的标的列表
        return lendList;
    }

    /**
     * 获取标的信息
     */
    @Override
    public Map<String, Object> getLendDetail(Long id) {
        // 查询标的对象
        Lend lend = baseMapper.selectById(id);

        // 组装数据
        String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());
        String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
        lend.getParam().put("returnMethod", returnMethod);
        lend.getParam().put("status", status);

        // 根据user_id获取借款人对象
        LambdaQueryWrapper<Borrower> wrapper = new LambdaQueryWrapper<Borrower>().eq(Borrower::getUserId, lend.getUserId());
        Borrower borrower = borrowerMapper.selectOne(wrapper);

        // 组装借款人对象
        BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerDetailVOById(borrower.getId());

        // 组装数据
        Map<String, Object> result = new HashMap<>();

        result.put("lend", lend);
        result.put("borrower", borrowerDetailVO);

        return result;
    }

    /**
     * 计算投资收益
     */
    @Override
    public BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalmonth, Integer returnMethod) {
        BigDecimal interestCount;

        // 计算总利息
        if (returnMethod.intValue() == ReturnMethodEnum.ONE.getMethod()) {
            interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalmonth);
        } else if (returnMethod.intValue() == ReturnMethodEnum.TWO.getMethod()) {
            interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalmonth);
        } else if (returnMethod.intValue() == ReturnMethodEnum.THREE.getMethod()) {
            interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalmonth);
        } else {
            interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalmonth);
        }

        return interestCount;
    }

    /**
     * 放款
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void makeLoan(Long lendId) {
        // 获取标的信息
        Lend lend = baseMapper.selectById(lendId);
        if (lend == null) {
            log.error("放款失败，标的ID不存在：{}", lendId);
            throw new BusinessException("标的信息不存在");
        }

        // 放款接口调用
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("agentId", HfbConst.AGENT_ID);
        paramMap.put("agentProjectCode", lend.getLendNo());// 标的编号
        String agentBillNo = LendNoUtils.getLoanNo();// 放款编号
        paramMap.put("agentBillNo", agentBillNo);

        // 平台收益，放款扣除，借款人借款实际金额=借款金额-平台收益
        // 月年化
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);

        // 平台实际收益 = 已投金额 * 月年化 * 标的期数
        BigDecimal realAmount = lend.getInvestAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        paramMap.put("mchFee", realAmount); // 商户手续费(平台实际收益)
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        String sign = RequestHelper.getSign(paramMap);
        paramMap.put("sign", sign);
        log.info("放款参数：" + JSONObject.toJSONString(paramMap));

        // 发送同步远程调用
        JSONObject result = RequestHelper.sendRequest(paramMap, HfbConst.MAKE_LOAD_URL);
        log.info("放款结果：" + result.toJSONString());

        // 放款失败
        if (!"0000".equals(result.getString("resultCode"))) {
            throw new BusinessException(result.getString("resultMsg"));
        }

        // 更新标的信息
        lend.setRealAmount(realAmount);
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setPaymentTime(LocalDateTime.now());
        baseMapper.updateById(lend);

        // 获取借款人信息
        Long userId = lend.getUserId();
        UserInfo userInfo = userInfoMapper.selectById(userId);
        if (userInfo == null) {
            log.error("放款失败，借款人信息不存在，标的ID：{}，用户ID：{}", lendId, userId);
            throw new BusinessException("借款人信息不存在");
        }
        String bindCode = userInfo.getBindCode();

        // 给借款账号转入金额
        BigDecimal total = new BigDecimal(result.getString("voteAmt"));
        userAccountMapper.updateAccount(bindCode, total, new BigDecimal(0));

        // 新增借款人交易流水
        TransFlowBO transFlowBO = new TransFlowBO(agentBillNo, bindCode, total, TransTypeEnum.BORROW_BACK, "借款放款到账，编号：" + lend.getLendNo());// 项目编号
        transFlowService.saveTransFlow(transFlowBO);

        // 获取投资列表信息
        List<LendItem> lendItemList = lendItemMapper.selectList(new LambdaQueryWrapper<LendItem>().eq(LendItem::getLendId, lendId));
        for (LendItem item : lendItemList) {
            // 获取投资人信息
            Long investUserId = item.getInvestUserId();
            UserInfo investUserInfo = userInfoMapper.selectById(investUserId);
            if (investUserInfo == null) {
                log.error("放款失败，投资人信息不存在，标的ID：{}，用户ID：{}", lendId, investUserId);
                continue; // 跳过当前投资人，继续处理其他投资人
            }
            String investBindCode = investUserInfo.getBindCode();

            // 投资人账号冻结金额转出
            BigDecimal investAmount = item.getInvestAmount(); // 投资金额
            userAccountMapper.updateAccount(investBindCode, new BigDecimal(0), investAmount.negate());

            // 新增投资人交易流水
            TransFlowBO investTransFlowBO = new TransFlowBO(LendNoUtils.getTransNo(), investBindCode, investAmount, TransTypeEnum.INVEST_UNLOCK, "冻结资金转出，出借放款，编号：" + lend.getLendNo());// 项目编号
            transFlowService.saveTransFlow(investTransFlowBO);
        }

        // 放款成功生成借款人还款计划和投资人回款计划
        this.repaymentPlan(lend);
    }

    /**
     * 还款计划
     */
    private void repaymentPlan(Lend lend) {
        // 还款计划列表
        List<LendReturn> lendReturnList = new ArrayList<>();

        // 按还款时间生成还款计划
        int len = lend.getPeriod().intValue();
        for (int i = 1; i <= len; i++) {
            // 创建还款计划对象
            LendReturn lendReturn = new LendReturn();
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());
            lendReturn.setBaseAmount(lend.getInvestAmount());
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setCurrentPeriod(i);// 当前期数
            lendReturn.setReturnMethod(lend.getReturnMethod());

            // 说明：还款计划中的这三项 = 回款计划中对应的这三项和：因此需要先生成对应的回款计划
            //          lendReturn.setPrincipal();
            //          lendReturn.setInterest();
            //          lendReturn.setTotal();

            lendReturn.setFee(new BigDecimal(0));
            lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i)); // 第二个月开始还款
            lendReturn.setIsOverdue(false);
            if (i == len) { // 最后一个月
                // 标识为最后一次还款
                lendReturn.setIsLast(true);
            } else {
                lendReturn.setIsLast(false);
            }
            lendReturn.setStatus(0);
            lendReturnList.add(lendReturn);
        }

        // 批量保存
        lendReturnService.saveBatch(lendReturnList);

        // 获取lendReturnList中还款期数与还款计划id对应map
        Map<Integer, Long> lendReturnMap = lendReturnList.stream().collect(Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId));

        //======================================================
        //=============获取所有投资者，生成回款计划===================
        //======================================================
        // 回款计划列表
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        // 获取投资成功的投资记录
        List<LendItem> lendItemList = lendItemMapper.selectList(new LambdaQueryWrapper<LendItem>().eq(LendItem::getLendId, lend.getId()).eq(LendItem::getStatus, 1));
        for (LendItem lendItem : lendItemList) {
            // 创建回款计划列表
            List<LendItemReturn> lendItemReturnList = this.returnInvest(lendItem.getId(), lendReturnMap, lend);
            lendItemReturnAllList.addAll(lendItemReturnList);
        }

        // 更新还款计划中的相关金额数据
        for (LendReturn lendReturn : lendReturnList) {
            BigDecimal sumPrincipal = lendItemReturnAllList.stream()
                    // 过滤条件：当回款计划中的还款计划id == 当前还款计划id的时候
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    // 将所有回款计划中计算的每月应收本金相加
                    .map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumInterest = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumTotal = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add);

            lendReturn.setPrincipal(sumPrincipal); // 每期还款本金
            lendReturn.setInterest(sumInterest); // 每期还款利息
            lendReturn.setTotal(sumTotal); // 每期还款本息
        }
        lendReturnService.updateBatchById(lendReturnList);
    }

    /**
     * 回款计划
     */
    public List<LendItemReturn> returnInvest(Long lendItemId, Map<Integer, Long> lendReturnMap, Lend lend) {
        // 投标信息
        LendItem lendItem = lendItemMapper.selectById(lendItemId);
        if (lendItem == null) {
            log.error("生成回款计划失败，投标记录不存在，ID：{}", lendItemId);
            return Collections.emptyList();
        }

        // 投资金额
        BigDecimal amount = lendItem.getInvestAmount();

        // 年化利率
        BigDecimal yearRate = lendItem.getLendYearRate();

        // 投资期数
        int totalMonth = lend.getPeriod();

        Map<Integer, BigDecimal> mapInterest = null;  // 还款期数 -> 利息
        Map<Integer, BigDecimal> mapPrincipal = null; // 还款期数 -> 本金

        // 根据还款方式计算本金和利息
        if (lend.getReturnMethod().intValue() == ReturnMethodEnum.ONE.getMethod()) {
            // 利息
            mapInterest = Amount1Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            // 本金
            mapPrincipal = Amount1Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.TWO.getMethod()) {
            mapInterest = Amount2Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount2Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.THREE.getMethod()) {
            mapInterest = Amount3Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount3Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else {
            mapInterest = Amount4Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount4Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        }

        // 校验计算结果是否完整
        if (mapInterest == null || mapInterest.isEmpty() || mapPrincipal == null || mapPrincipal.isEmpty()) {
            log.error("生成回款计划失败，计算结果为空，投标记录ID：{}", lendItemId);
            return Collections.emptyList();
        }

        // 创建回款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();
        for (int currentPeriod = 1; currentPeriod <= totalMonth; currentPeriod++) {
            // 根据还款期数获取还款计划的id
            Long lendReturnId = lendReturnMap.get(currentPeriod);
            if (lendReturnId == null) {
                log.error("生成回款计划失败，找不到对应的还款计划，期数：{}，投标记录ID：{}", currentPeriod, lendItemId);
                continue;
            }

            LendItemReturn lendItemReturn = new LendItemReturn();
            lendItemReturn.setLendReturnId(lendReturnId);
            lendItemReturn.setLendItemId(lendItemId);
            lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
            lendItemReturn.setLendId(lendItem.getLendId());
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());
            lendItemReturn.setLendYearRate(lend.getLendYearRate());
            lendItemReturn.setCurrentPeriod(currentPeriod);
            lendItemReturn.setReturnMethod(lend.getReturnMethod());

            // 本金和利息计算
            BigDecimal principal;
            BigDecimal interest;

            if (currentPeriod == lend.getPeriod().intValue()) {
                // 最后一期本金计算
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                principal = lendItem.getInvestAmount().subtract(sumPrincipal);

                // 最后一期利息计算
                interest = Optional.ofNullable(mapInterest.get(currentPeriod)).orElse(BigDecimal.ZERO);
            } else {
                // 非最后一期本金和利息
                principal = Optional.ofNullable(mapPrincipal.get(currentPeriod)).orElse(BigDecimal.ZERO);
                interest = Optional.ofNullable(mapInterest.get(currentPeriod)).orElse(BigDecimal.ZERO);
            }

            // 设置本金和利息
            lendItemReturn.setPrincipal(principal);
            lendItemReturn.setInterest(interest);

            // 计算总金额
            lendItemReturn.setTotal(principal.add(interest));
            lendItemReturn.setFee(new BigDecimal("0"));
            lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));

            // 是否逾期，默认未逾期
            lendItemReturn.setIsOverdue(false);
            lendItemReturn.setStatus(0);

            // 记录日志
            log.debug("生成回款计划：投标记录ID={}, 期数={}, 本金={}, 利息={}, 总额={}",
                    lendItemId, currentPeriod, principal, interest, lendItemReturn.getTotal());

            lendItemReturnList.add(lendItemReturn);
        }

        // 批量保存回款计划
        if (!lendItemReturnList.isEmpty()) {
            lendItemReturnService.saveBatch(lendItemReturnList);
        }

        return lendItemReturnList;
    }
}